﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using uk.org.aspellclark.todolistppc;
using System.Xml;
using Google.GData.Spreadsheets;
using System.Collections;
using ConsoleApplication1;
using Google.GData.Client;

namespace SyncToolProgram
{
    public class Sync
    {
        private static ArrayList allWorksheets = new ArrayList();
        private static List<SyncTask> Tasks = new List<SyncTask>();

        public void GetTasksFromXML(string FileName)
        {
            bool failtoopen = false;

            XmlDocument xmlTodoList;
            XmlNode xndList;
            TodoListTreeNode tnParent;

            try
            {
                xmlTodoList = new XmlDocument();

                xmlTodoList.Load(FileName);

                if (xmlTodoList.GetElementsByTagName("TODOLIST").Count > 0)
                {
                    xndList = xmlTodoList.GetElementsByTagName("TODOLIST").Item(0);


                    if (xndList.HasChildNodes == true)
                    {
                        for (int i = 0; i < xndList.ChildNodes.Count; i++)
                        {
                            AppendTask(null, xndList.ChildNodes.Item(i));
                        }
                    }
                }
            }
            catch (Exception e)
            {
                failtoopen = true;
            }
        }

        public void SaveTasksToGoogleSpreadSheet(string UserName, string Password)
        {
            SpreadsheetsService service = new SpreadsheetsService("exampleCo-exampleApp-1");
            service.setUserCredentials(UserName, Password);
            
            // Demonstrate printing all spreadsheets and worksheets.
            PrintAllSpreadsheetsAndWorksheets(service);

            // Demonstrate retrieving the list feed for a single worksheet,
            // with the rows (ordered by position) reversed.
            int userChoice = 1;
            WorksheetEntry entry = allWorksheets[userChoice] as WorksheetEntry;
            int TaskNumber = 0;
            
            //Get SpreadsheetRows
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);
            ListQuery query = new ListQuery(listFeedLink.HRef.ToString());
            query.OrderByPosition = true;
            query.Reverse = true;
            ListFeed feed = service.Query(query);

            foreach (SyncTask Task in Tasks)
            {
                
                TaskNumber++;
                bool TaskExistsInSpreadsheet = false;

                foreach (ListEntry GoogleRow in feed.Entries)
                {
                    if (Convert.ToInt32(GoogleRow.Elements[0].Value) == Task.TaskId)
                    {
                        DateTime epoch = new DateTime(1970, 1, 1);
                        DateTime LastMod = epoch.AddSeconds(double.Parse(GoogleRow.Elements[12].Value.Substring(0, 10)));
                        if (LastMod < Task.LastMod)
                        {
                            GoogleRow.Elements[0].Value = Task.TaskId.ToString();
                            GoogleRow.Elements[1].Value = Task.Title;
                            GoogleRow.Elements[12].Value = (Task.LastMod - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds.ToString() + "000";
                            UpdateRow(service,GoogleRow);
                        }
                        TaskExistsInSpreadsheet = true;
                    }
                }
                if(!TaskExistsInSpreadsheet)
                {
                    
                    ListEntry insertedEntry = InsertRow(service, entry, Task);
                    
                    //service.Insert(entry.Feed,);
                }
            }
        }

        public bool GetTasksFromGoogleSpreadsheet(string UserName, string Password)
        {
            SpreadsheetsService service = new SpreadsheetsService("exampleCo-exampleApp-1");
            service.setUserCredentials(UserName, Password);

            // Demonstrate printing all spreadsheets and worksheets.
            if (!PrintAllSpreadsheetsAndWorksheets(service))
            {
                return false;
            }

            // Demonstrate retrieving the list feed for a single worksheet,
            // with the rows (ordered by position) reversed.
            int userChoice = 1;
            WorksheetEntry entry = allWorksheets[userChoice] as WorksheetEntry;
            Tasks.Clear();
            RetrieveListFeed(service, entry, true);

            return true;
        }

        public void SaveTasksToXML(string FileName)
        {
            ToDoList oTodoList = new ToDoList();
            oTodoList.writeTodoListFile(FileName, Tasks);
        }



        private void AppendTask(TodoListTreeNode tnParent, XmlNode xndTask)
        {

 
                TodoListTreeNode tnChild;
                SyncTask clsTask;

                if (xndTask.Name.ToUpper() == "TASK")
                {
                    clsTask = new SyncTask();
                    clsTask.readFromXml(xndTask);
                    //if task already exists just update it else create a new task


                        tnChild = new TodoListTreeNode(clsTask.Title);
                        tnChild.TaskId = clsTask.TaskId;
                        tnChild.Task = clsTask;
                        if (tnParent != null)
                        {

                            tnChild.ParentTaskId = tnParent.TaskId;
                            clsTask.ParentTaskId = tnParent.TaskId;
                        }

                        if (xndTask.HasChildNodes == true)
                        {
                            for (int i = 0; i < xndTask.ChildNodes.Count; i++)
                            {
                                AppendTask(tnChild, xndTask.ChildNodes.Item(i));
                            }
                        }
                        tnChild.Checked = clsTask.Complete;
                        AddSyncTask(clsTask);
                }
                else if (xndTask.Name.ToUpper() == "COMMENTS")
                {
                    if (tnParent != null)
                    {
                        clsTask = tnParent.Task;
                        clsTask.CommentsStr = xndTask.InnerText;
                        tnParent.Task = clsTask;
                    }
                }
            

        }

        private static void AddSyncTask(SyncTask NewTask)
        {
            if (Tasks.Where(t => t.TaskId == NewTask.TaskId).Count() == 1)
            {
                SyncTask ExistingTask = Tasks.Single(t => t.TaskId == NewTask.TaskId);
                if (NewTask.LastMod > ExistingTask.LastMod)
                {
                    Tasks[Tasks.IndexOf(ExistingTask)] = NewTask;
                }
            }
            else
            {
                Tasks.Add(NewTask);
            }
        }


        #region Google Methods
        /// <summary>
        /// Prints a list of all worksheets in the specified spreadsheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the spreadsheet whose worksheets are to be retrieved</param>
        private void PrintAllWorksheets(SpreadsheetsService service,
            SpreadsheetEntry entry)
        {
            AtomLink link = entry.Links.FindService(GDataSpreadsheetsNameTable.WorksheetRel, null);

            WorksheetQuery query = new WorksheetQuery(link.HRef.ToString());
            WorksheetFeed feed = service.Query(query);

            Console.WriteLine("Worksheets in " + entry.Title.Text + ":");
            foreach (WorksheetEntry worksheet in feed.Entries)
            {
                allWorksheets.Add(worksheet);
                Console.WriteLine("  " + worksheet.Title.Text);
                
            }
            Console.WriteLine();
        }

        /// <summary>
        /// Prints a list of all the user's spreadsheets, and the
        /// list of worksheets that each spreadsheet contains.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        private bool PrintAllSpreadsheetsAndWorksheets(SpreadsheetsService service)
        {
            SpreadsheetQuery query = new SpreadsheetQuery();
            try
            {
                SpreadsheetFeed feed = service.Query(query);

            Console.WriteLine("Your spreadsheets:");
            foreach (SpreadsheetEntry entry in feed.Entries)
            {
                Console.WriteLine("Spreadsheet: {0}", entry.Title.Text);
                PrintAllWorksheets(service, entry);
            }
            }
            catch(Exception ex)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Retrieves and prints a list feed of the specified worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet to retrieve</param>
        /// <param name="reverseRows">true if the rows in the worksheet should
        /// be reversed when returned from the server</param>
        private void RetrieveListFeed(SpreadsheetsService service, WorksheetEntry entry,
            bool reverseRows)
        {
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

            Console.WriteLine();
            Console.WriteLine("This worksheet's list feed URL is:");
            Console.WriteLine(listFeedLink.HRef);

            ListQuery query = new ListQuery(listFeedLink.HRef.ToString());
            if (reverseRows)
            {
                query.OrderByPosition = true;
                query.Reverse = true;
            }
            ListFeed feed = service.Query(query);

            Console.WriteLine();
            Console.WriteLine("Worksheet has {0} rows:", feed.Entries.Count);

            foreach (ListEntry worksheetRow in feed.Entries)
            {
                AddSyncTask(new SyncTask(worksheetRow));

                ListEntry.CustomElementCollection elements = worksheetRow.Elements;
                foreach (ListEntry.Custom element in elements)
                {
                    Console.Write(element.Value + "\t");
                }
                Console.WriteLine();
            }
        }

        /// <summary>
        /// Executes a structured query against the list feed of
        /// the specified worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet to query</param>
        /// <param name="queryText">the structured query</param>
        private void StructuredQuery(SpreadsheetsService service, WorksheetEntry entry,
            string queryText)
        {
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

            ListQuery query = new ListQuery(listFeedLink.HRef.ToString());
            query.SpreadsheetQuery = queryText;
            ListFeed feed = service.Query(query);

            Console.WriteLine();
            Console.WriteLine("{0} rows matched your query:", feed.Entries.Count);
            foreach (ListEntry worksheetRow in feed.Entries)
            {
                ListEntry.CustomElementCollection elements = worksheetRow.Elements;
                foreach (ListEntry.Custom element in elements)
                {
                    Console.Write(element.Value + "\t");
                }
                Console.WriteLine();
            }
        }

        /// <summary>
        /// Inserts a new row in the specified worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet into which the row will be inserted</param>
        /// <returns>the inserted ListEntry object, representing the new row</returns>
        private ListEntry InsertRow(SpreadsheetsService service, WorksheetEntry entry, SyncTask Task)
        {
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

            ListQuery query = new ListQuery(listFeedLink.HRef.ToString());
            ListFeed feed = service.Query(query);

            ListEntry firstRow = feed.Entries[0] as ListEntry;
            ListEntry newRow = new ListEntry();

            Console.WriteLine();
            Console.WriteLine("Inserting a new row...");
            foreach (ListEntry.Custom element in firstRow.Elements)
            {
                String elementValue = "";
                if (element.LocalName == "title")
                {
                    elementValue = Task.Title;
                }
                else if (element.LocalName == "id")
                {
                    elementValue = Task.TaskId.ToString();
                }
                else if (element.LocalName == "parentid")
                {
                    elementValue = Task.ParentTaskId.ToString();
                }
                else if (element.LocalName == "priority")
                {
                    elementValue = "1";
                }
                else if (element.LocalName == "modified")
                {
                    //(Task.LastMod.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
                    elementValue = ((Task.LastMod.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString() + "000";
                }
                else
                {
                    //Console.Write("Enter the value of column \"{0}\": ", element.LocalName);
                    //elementValue = Console.ReadLine();
                }
                ListEntry.Custom curElement = new ListEntry.Custom();
                curElement.LocalName = element.LocalName;
                curElement.Value = elementValue;

                newRow.Elements.Add(curElement);
            }

            ListEntry insertedRow = feed.Insert(newRow);
            Console.WriteLine("Successfully inserted new row: \"{0}\"",
                insertedRow.Content.Content);

            return insertedRow;
        }

        /// <summary>
        /// Updates the value of a cell in a single worksheet row.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the ListEntry representing the row to update</param>
        /// <returns>the updated ListEntry object</returns>
        private ListEntry UpdateRow(SpreadsheetsService service, ListEntry entry)
        {
            //ListEntry.Custom firstColumn = entry.Elements[0];

            //Console.WriteLine();
            //Console.Write("Enter a new value for \"{0}\" (currently \"{1}\"): ", firstColumn.LocalName, firstColumn.Value);
            //String newValue = Console.ReadLine();

            //firstColumn.Value = newValue;

            ListEntry updatedRow = entry.Update() as ListEntry;

            //Console.WriteLine("Successfully updated \"{0}\": \"{1}\"", updatedRow.Elements[0].LocalName, updatedRow.Elements[0].Value);

            return updatedRow;
        }

        /// <summary>
        /// Demonstrates retrieving and printing the cell feed for a
        /// worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet whose cell feed is to be retrieved</param>
        private void RetrieveCellFeed(SpreadsheetsService service, WorksheetEntry entry)
        {
            AtomLink cellFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.CellRel, null);

            Console.WriteLine();
            Console.WriteLine("This worksheet's cells feed URL is:");
            Console.WriteLine(cellFeedLink.HRef);

            CellQuery query = new CellQuery(cellFeedLink.HRef.ToString());
            CellFeed feed = service.Query(query);

            Console.WriteLine();
            Console.WriteLine("Cells in this worksheet:");
            foreach (CellEntry curCell in feed.Entries)
            {
                Console.WriteLine("Row {0}, column {1}: {2}", curCell.Cell.Row,
                    curCell.Cell.Column, curCell.Cell.Value);
            }
        }

        /// <summary>
        /// Performs a cell range query on the specified worksheet to
        /// retrieve only the cells in the first column.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet to retrieve</param>
        private void CellRangeQuery(SpreadsheetsService service, WorksheetEntry entry)
        {
            AtomLink cellFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.CellRel, null);

            CellQuery query = new CellQuery(cellFeedLink.HRef.ToString());
            query.MinimumColumn = 1;
            query.MaximumColumn = 1;
            query.MinimumRow = 2;

            CellFeed feed = service.Query(query);
            Console.WriteLine();
            Console.WriteLine("Cells in column 1:");
            foreach (CellEntry curCell in feed.Entries)
            {
                Console.WriteLine("Row {0}: {1}", curCell.Cell.Row, curCell.Cell.Value);
            }
        }

        /// <summary>
        /// Updates a single cell in the specified worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet to update</param>
        private void UpdateCell(SpreadsheetsService service, WorksheetEntry entry)
        {
            AtomLink cellFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.CellRel, null);
            CellQuery query = new CellQuery(cellFeedLink.HRef.ToString());
            Console.WriteLine();

            Console.Write("Row of cell to update? ");
            string row = Console.ReadLine();

            Console.Write("Column of cell to update? ");
            string column = Console.ReadLine();

            query.MinimumRow = query.MaximumRow = uint.Parse(row);
            query.MinimumColumn = query.MaximumColumn = uint.Parse(column);

            CellFeed feed = service.Query(query);
            CellEntry cell = feed.Entries[0] as CellEntry;

            Console.WriteLine();
            Console.WriteLine("Current cell value: {0}", cell.Cell.Value);
            Console.Write("Enter a new value: ");
            string newValue = Console.ReadLine();

            cell.Cell.InputValue = newValue;
            AtomEntry updatedCell = cell.Update();

            Console.WriteLine("Successfully updated cell: {0}", updatedCell.Content.Content);
        }

        /// <summary>
        /// Prompts the user for a number representing one of their
        /// worksheets; this worksheet will then be used to demonstrate
        /// the various Spreadsheets operations above.
        /// </summary>
        /// <returns>the number of the worksheet chosen by the user</returns>
        private int GetUserWorksheetChoice()
        {
            Console.WriteLine("Select the worksheet on which to demonstrate");
            Console.WriteLine("add/edit/delete operations by entering its number:");
            Console.WriteLine();
            for (int i = 0; i < allWorksheets.Count; i++)
            {
                WorksheetEntry entry = allWorksheets[i] as WorksheetEntry;
                Console.WriteLine("{0}: {1}", i + 1, entry.Title.Text);
            }

            Console.WriteLine();
            Console.Write("Your choice: ");
            String userResponse = "2";

            return int.Parse(userResponse) - 1;
        }

        #endregion
    }
}
